home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / SED15.ARJ / SEDCOMP.C < prev    next >
Text File  |  1991-09-28  |  28KB  |  670 lines

  1. /*
  2.  * sedcomp.c -- stream editor main and compilation phase
  3.  *
  4.  * The stream editor compiles its command input  (from files or -e options) into
  5.  * an internal form using compile() then executes the compiled form using
  6.  * execute(). Main() just initializes data structures, interprets command
  7.  * line options, and calls compile() and execute() in appropriate sequence.
  8.  * The data structure produced by compile() is an array of compiled-command
  9.  * structures (type sedcmd).  These contain several pointers into pool[], the
  10.  * regular-expression and text-data pool, plus a command code and g & p
  11.  * flags. In the special case that the command is a label the struct  will
  12.  * hold a ptr into the labels array labels[] during most of the compile,
  13.  * until resolve() resolves references at the end. The operation of execute()
  14.  * is described in its source module.
  15.  *
  16.  * ==== Written for the GNU operating system by Eric S. Raymond ====
  17.  * v1.1, 19 Jun 91
  18.  * Toad Hall Tweak
  19.  * - Mostly minor tweaks to make it compile with Borland's TC v2.0.
  20.  * - No more feelthy debug.
  21.  * - We're prototyping now.
  22.  * See VERSION.NOT for details.
  23.  *
  24.  * modified Sep 91 by Howard Helman (h**2) for BC++ and Sun4 plus fixes:
  25.  *   (those marked **** were critical)
  26.  *     1. l command cleanup (indexing and quoting)
  27.  *     2. first line problem (should have been delete FALSE) ****
  28.  *    3. y command compile funny BC++ problem with chars and ints
  29.  *    4. fixed `\' escapes in patterns
  30.  *    5. fixed `\' escapes in rhs
  31.  *      6. fixed `\' escapes in y strings 
  32.  *      7. fixed `\' escapes in inserted text
  33.  *      8. fixed `\' in sets  (all fixed by fixquote routine)
  34.  *      9. RE bad looping on error message   *****
  35.  *     10. reworked entire selected routine
  36.  *     11. spaces after -e -f and nothing after -g -n
  37.  *     12. errors to stderr and general error message fixups
  38.  *     13. usage message when no args
  39.  *     14. Make it compile under Sun Unix with minimum lint complaints
  40.  *     15. Make it compile under BC++ 2.0   *****
  41.  *     16. Fix recognition of last line to edit
  42.  *     17. ; # and initial space clean ups
  43.  *     18. No `\` escapes in file names or labels
  44.  *     19. Last line may not have \n in commands
  45.  *     20. 256 bit characters in all contexts
  46.  *     21. Add + option to second address
  47.  *     22. allow \{m,n\} RE's including after \1 as for *; + now \{1,\}
  48.  *     23. allow \<  and \> RE's
  49.  *     24. Genbuf now extremly long to hold everything(was 71!!) *****
  50.  *     25. Misc cleanups for n, N, & D commands range checks cleaned up.
  51.  *     26. Reset inrange flag on exit from {} nesting
  52.  *     27. Blanks after : (actually all of label processing fixed up
  53.  *     28. - in character character sets now works for ranges
  54.  *     29. g flag in s command cleanup used ++ instead of = 1
  55.  *     30. made separate -e and -f input routines and fixed up gettext
  56.  *     31. RELIMIT replaced by poolend  allows REs to be of any size
  57.  *     32. \0 character is now an error in an RE body
  58.  *     33. address of 000 now illegal
  59.  *     34. trailing arguments of s command handled properly
  60.  *     35. & substitutions fixed(previously could not escape)
  61.  *     36. handling of lastre
  62.  *     37. % as repeat last rhs added
  63.  *     38. nth substitution only added to s command
  64.  *     39. \?RE? in addresses added
  65.  *     40. No range on { command
  66.     v1.4, Toad Hall, 20 Sep 91
  67.  */
  68. #ifdef OTHER 
  69. #include "compiler.h"
  70. #include "debug.h"
  71. #endif
  72. #ifdef LATTICE
  73. #define void int
  74. #endif
  75.  
  76. #include <stdio.h>        /* uses getc, fprintf, fopen, fclose */
  77. #include "sed.h"        /* command type struct and name defines */
  78.  
  79. /***** public stuff ******/
  80. #define MAXCMDS        200    /* maximum number of compiled commands */
  81. #define MAXLINES    256    /* max # numeric addresses to compile */
  82.  
  83. /* main data areas */
  84. char    linebuf[MAXBUF + 1];    /* current-line buffer */
  85. sedcmd    cmds[MAXCMDS + 1];    /* hold compiled commands */
  86. long    linenum[MAXLINES];    /* numeric-addresses table */
  87.  
  88. /* miscellaneous shared variables */
  89. int         nflag;     /* -n option flag */
  90. static int  gflag;    /* -g option flag */
  91. int         eargc;     /* scratch copy of argument count */
  92. sedcmd    *pending = NULL;    /* next command to be executed */
  93. char    bits[] = {1, 2, 4, 8, 16, 32, 64, 128};
  94.  
  95. /***** module common stuff *****/
  96. #define POOLSIZE    10000    /* size of string-pool space */
  97. #define WFILES        10    /* max # w output files that can be compiled */
  98. #define MAXDEPTH    20    /* maximum {}-nesting level */
  99. #define MAXLABS        50    /* max # of labels that can be handled */
  100.  
  101. #define SKIPWS(pc) while((*pc==' ')||(*pc=='\t')||*pc=='\f'||*pc=='\v')pc++
  102. #define ABORT(msg)    (fprintf(stderr, msg, linebuf), exit(2))
  103. #define IFEQ(x, v)    if (*x == v) x++ ,    /* do expression */
  104.  
  105. /* error messages */
  106. static char     BADGCNT[]="sed: bad value for match count on s command %s\n";
  107. static char    AGMSG[] = "sed: garbled address %s\n";
  108. static char    CGMSG[] = "sed: garbled command %s\n";
  109. static char    TMTXT[] = "sed: too much text: %s\n";
  110. static char    AD1NG[] = "sed: no addresses allowed for %s\n";
  111. static char    AD2NG[] = "sed: only one address allowed for %s\n";
  112. static char    TMCDS[] = "sed: too many commands, last was %s\n";
  113. static char    COCFI[] = "sed: cannot open command-file %s\n";
  114. static char    UFLAG[] = "sed: unknown flag %c\n";
  115. static char    CCOFI[] = "sed: cannot create %s\n";
  116. static char    ULABL[] = "sed: undefined label %s\n";
  117. static char    TMLBR[] = "sed: too many {'s %s\n";
  118. static char    NSCAX[] = "sed: no such command as %s\n";
  119. static char    TMRBR[] = "sed: too many }'s %s\n";
  120. static char    DLABL[] = "sed: duplicate label %s\n";
  121. static char    TMLAB[] = "sed: too many labels: %s\n";
  122. static char    TMWFI[] = "sed: too many w files %s \n";
  123. static char    REITL[] = "sed: RE too long: %s\n";
  124. static char    TMLNR[] = "sed: too many line numbers %s\n";
  125. static char    TRAIL[] = "sed: command \"%s\" has trailing garbage\n";
  126. static char  USAGE[] = "usage: sed [-n] [-g] [-e cmds] [-f cmdfile] files\n";
  127. static char    NEEDB[] = "sed: error proccessing: %s\n"; /*hh 12*/
  128. static char     NOARG[] = "sed: no argument for -e\n";    /*hh 12*/
  129. static char     ILFQT[] = "sed: bad expression %4.4s\n";  /*hh 12*/
  130. static char     BADRANGE[]= "sed: range error in set %s\n";
  131.  
  132. typedef struct {        /* represent a command label */
  133.     char    *name;        /* the label name */
  134.     sedcmd    *list;        /* it's on the label search list */
  135.     sedcmd    *address;    /* pointer to the cmd it labels */
  136.   }  label;
  137.  
  138. /* label handling */
  139. static label    labels[MAXLABS];    /* here's the label table */
  140. static label   *curlab = labels + 1;    /* pointer to current label */
  141.  
  142. /* string pool for regular expressions, append text, etc. etc. */
  143. static char    pool[POOLSIZE];        /* the pool */
  144. static char    *fp = pool;            /* current pool pointer */
  145. static char    *poolend = pool + POOLSIZE;    /* pointer past pool end */
  146.  
  147. /* compilation state */
  148. static FILE    *cmdf = NULL;    /* current command source */
  149. static char    *cp = linebuf;    /* compile pointer */
  150. static sedcmd  *cmdp = cmds;    /* current compiled-cmd ptr */
  151. static char    *lastre = NULL;    /* old RE pointer */
  152. static char    *lastrhs= NULL;  /* old RHS pointer*/
  153. static int    bdepth = 0;    /* current {}-nesting level */
  154. static int    bcount = 0;    /* # tagged patterns in current RE */
  155. static char   **eargv;        /* scratch copy of argument list */
  156.  
  157. /* imported functions */
  158. #ifdef __TURBOC__            /* v1.1 */
  159. #include <string.h>
  160. #include <stdlib.h>            /* exit() */
  161. #include <ctype.h>
  162. #define Void void        /*K&R and Std C compatibility*/
  163. static void   compile(int eflag),einit(void);
  164. static void   resolve(void);
  165. extern void   execute(char *file);/* execute compiled command  (in SEDEXEC.C) */
  166. static char   fixquote(char**);
  167. static int    ecmdline(void), fcmdline(void);
  168. static int    address(char **expbuf,int pass);
  169. static int    cmdcomp(register char cchar);
  170. static char  *gettext(register char *txp,int doq);
  171. static label *search(void);
  172. static int    recomp(char **expbuf, char redelim);
  173. static int    rhscomp(char **rhsp, char delim);
  174. static int    ycomp(void);
  175. static char   tox(char);
  176. static int    processm(void);
  177. #else    /* !__TURBOC__*/
  178. #define Void
  179. extern int    strcmp();
  180. static int    recomp(), address(), rhscomp(),ycomp(),processm();
  181. extern void   execute();    /* execute compiled command */
  182. static char  *gettext();
  183. static label *search();
  184. static char   fixquote();
  185. static void   compile(), resolve(), einit();
  186. #endif    /* ?__TURBOC__ */
  187.  
  188. #ifdef HHDEB
  189. void mybcheck(Void){char *p; for(p=pool;p<fp;p++)
  190.    if((*p&0xff)<' ')printf("%2i,",*p&0xff);
  191.    else printf("%c,",*p&0xff);
  192.  getchar();}
  193. #else
  194. void mybcheck(Void){}
  195. #endif
  196.  
  197. int main(argc,argv) int argc; char *argv[];{ /*hh 15*/
  198.     eargc = argc;        /* set local copy of argument count */
  199.     eargv = argv;        /* set local copy of argument list */
  200.     if (eargc <= 1){fprintf(stderr,USAGE);exit(1);} /*hh 13*/
  201.     PASS("main(): setup"); /*scan through the arguments,interpreting each*/
  202.     while ((--eargc > 0) && (**++eargv == '-'))
  203.         switch (eargv[0][1]) {
  204.          case 'e':
  205.             if(eargv[0][2]){eargc++;*eargv+=2;eargv--;}/*hh 11*/
  206.             einit();compile(1);    /* compile with e flag on */
  207.             break;        /* get another argument */
  208.          case 'f':
  209.             if(eargv[0][2]){ /*hh 11*/
  210.               if((cmdf=fopen(*eargv+2,"rt"))==NULL){ /*hh 12*/
  211.                  fprintf(stderr,COCFI,*eargv+2);exit(2);}}
  212.             else if (eargc-- <= 0){    /*hh 12*/
  213.               fprintf(stderr,NEEDB,eargv[0]);exit(2);}
  214.             else if ((cmdf = fopen(*++eargv, "rt")) == NULL) {
  215.               fprintf(stderr, COCFI, *eargv);exit(2);}
  216.             compile(0);        /* file is O.K., compile it */
  217.             fclose(cmdf);
  218.             break;    /* go back for another argument */
  219.           case 'g':  gflag++;    /* set global flag on all s cmds */
  220.             if(eargv[0][2])
  221.               {fprintf(stderr,NEEDB,eargv[0]);exit(2);}/*hh 11*/
  222.             break;
  223.           case 'n':  nflag++;    /* no print except on p flag or w */
  224.             if(eargv[0][2])
  225.               {fprintf(stderr,NEEDB,eargv[0]);exit(2);}/*hh 11*/
  226.             break;
  227.           default:
  228.             fprintf(stderr, UFLAG, eargv[0][1]);exit(1);/*hh 11*/}
  229.     PASS("main(): argscan");
  230.     if (cmdp == cmds) {        /* no commands have been compiled */
  231.         eargv--; eargc++; einit(); compile(1); eargv++; eargc--;}
  232.     if (bdepth) ABORT(TMLBR);    /* we have unbalanced squigglies */
  233.     resolve(); mybcheck();      /* resolve label table indirections */
  234.     if (eargc <= 0)    execute((char*)NULL);/*execute on file from stdin only*/
  235.     else  while (--eargc >= 0)    /* else execute only listed files */
  236.               execute(*eargv++);
  237.     PASS("main(): end & exit OK");
  238.     return (0);  }        /* everything was O.K. if we got here */
  239.  
  240. #define H    0x80    /* 128 bit, on if there's really code for  command */
  241. #define LOWCMD  56    /* = '8', lowest char indexed in cmdmask */
  242. /* indirect through this to get command internal code, if it exists */
  243. static char        cmdmask[] =
  244. {0,        0,     H,      0,      0,H+EQCMD,    0,      0, /* 89:;<=>? */
  245.  0,        0,     0,      0,  CDCMD,      0,    0,  CGCMD, /* @ABCDEFG */
  246.  CHCMD,    0,     0,      0,      0,      0,CNCMD,      0, /* HIJKLMNO */
  247.  CPCMD,    0,     0,      0,H+CTCMD,      0,    0,H+CWCMD, /* PQRSTUVW */
  248.  0,        0,     0,      0,      0,      0,    0,      0, /* XYZ[\]^_ */
  249.  0,   H+ACMD,H+BCMD, H+CCMD,   DCMD,      0,    0,   GCMD, /* `abcdefg */
  250.  HCMD,H+ICMD,     0,     0,  H+LCMD,      0, NCMD,      0, /* hijklmno */
  251.  PCMD,H+QCMD,H+RCMD, H+SCMD, H+TCMD,      0,    0, H+WCMD, /* pqrstuvw */
  252.  XCMD,H+YCMD,     0,H+BRCMD,     0,       H,    0,      0,};/*xyz{|}~  */
  253.  
  254. static void compile(eflag)int eflag; /*hh 14*//* precompile sed commands*/
  255.        {char ccode; static int comment1=0;
  256.     PASS("compile(): entry");
  257.     while(*cp=='#'||*cp++==';'||(eflag?ecmdline():fcmdline())){
  258.         SKIPWS(cp);
  259.             if (*cp == '#'){*cp=0;if(!comment1++&&cp[1]=='n')nflag++;}
  260.         if (*cp == '\0' || *cp==';') continue;
  261.         /* compile first address */
  262.         if (fp > poolend) ABORT(TMTXT);
  263.         if (address(&cmdp->addr1,1)){  SKIPWS(cp);
  264.            if (*cp == ',' || *cp == ';') {    /* there's 2nd addr */
  265.            cp++; if (fp > poolend) ABORT(TMTXT);
  266.            if(!address(&cmdp->addr2,2))    ABORT(AGMSG);}}
  267.         if (fp > poolend)  ABORT(TMTXT);
  268.         SKIPWS(cp);        /* discard whitespace after address */
  269.         IFEQ(cp, '!') cmdp->flags.allbut = 1;
  270.         SKIPWS(cp);        /* get cmd char, range-check it */
  271.         if ((*cp < LOWCMD) || (*cp > '~')
  272.           || ((ccode = cmdmask[*cp - LOWCMD]) == 0))ABORT(NSCAX);
  273.         cmdp->command = ccode & ~H;    /* fill in command value */
  274.         if ((ccode & H) == 0)        /* if no compile-time code */
  275.             cmdp++,cp++;        /* end cmd &discard char */
  276.         else cmdp+=cmdcomp(*cp++);/*execute stuff and bump if gotone*/
  277.         if (cmdp >= cmds + MAXCMDS)  ABORT(TMCDS);
  278.         SKIPWS(cp);            /* look for trailing stuff */
  279.         if (*cp != '\0'&&*cp!=';'&&*cp!='#') ABORT(TRAIL); /*hh 17*/}}
  280.  
  281. static int cmdcomp(cchar) /* compile a single command */
  282.     register char   cchar;    /* character name of command */
  283. {   
  284.     static sedcmd **cmpstk[MAXDEPTH];    /* current cmd stack for {} */
  285.     static char    *fname[WFILES];        /* w file name pointers */
  286.     static FILE    *fout[WFILES] ;          /* w file file ptrs */
  287.     static int    nwfiles = 0;             /* count of open w files */
  288.     int            i;            /* indexing dummy used in w */
  289.     label           *lpt;
  290.     char        redelim;        /* current RE delimiter */
  291.  
  292.     switch (cchar) {
  293.       case '{':            /* start command group */
  294.         cmdp->flags.allbut = !cmdp->flags.allbut;
  295.         cmpstk[bdepth++] = &(cmdp->u.link);
  296.         if (++cmdp >= cmds + MAXCMDS) ABORT(TMCDS);
  297.         if (*cp != '\0')
  298.             *--cp = ';'; /* get next cmd w/o lineread *//*hh 17*/
  299.         return (0);
  300.       case '}':                   /* end command group */
  301.         if (cmdp->addr1||cmdp->flags.allbut)  
  302.                          ABORT(AD1NG);/* no addresses allowed */
  303.         if (--bdepth < 0) ABORT(TMRBR);/* too many right braces */
  304.         *cmpstk[bdepth] = cmdp;        /* set the jump address */
  305.         return (0);
  306.       case '=':            /* print current source line number */
  307.       case 'q':            /* exit the stream editor */
  308.         if (cmdp->addr2) ABORT(AD2NG);
  309.         break;
  310.       case ':':            /* label declaration */
  311.                 if(cmdp->addr1) ABORT(AD1NG);
  312.                 if((lpt=search())->address) ABORT(DLABL);
  313.                 lpt->address=cmdp;   /*mark it here*/
  314.         return (0);
  315.       case 'b':            /* branch command */
  316.       case 't':            /* branch-on-succeed command */
  317.       case 'T':            /* branch-on-fail command */
  318.         cmdp->u.link=(lpt=search())->list;
  319.                 lpt->list=cmdp;
  320.                 break;
  321.       case 'a':            /* append text */
  322.       case 'i':            /* insert text */
  323.       case 'r':            /* read file into stream */
  324.         if (cmdp->addr2) ABORT(AD2NG);
  325.       case 'c':            /* change text */
  326.         if ((*cp == '\\') && (cp[1] == '\n')) cp+=2;
  327.         fp = gettext(cmdp->u.lhs = fp,cchar!='r'); /*hh 7*/
  328.         break;
  329.       case 's':        /* substitute regular expression */
  330.         redelim = *cp++;    /* get delimiter from 1st ch */
  331.         if(!recomp(&cmdp->u.lhs,redelim))ABORT(CGMSG);
  332.         if ((cmdp->rhs = fp) > poolend)    ABORT(TMTXT);
  333.         if (!rhscomp(&cmdp->rhs, redelim)) ABORT(CGMSG);
  334.         if (gflag) cmdp->flags.global = 1;
  335.                 while(*cp){
  336.                     SKIPWS(cp);
  337.             if(isdigit(*cp)){
  338.                       i=0;while(isdigit(*cp)) i=i*10+*cp++-'0';
  339.                       if (!i||i>512)ABORT(BADGCNT);
  340.                       cmdp->flags.nthone=i;}
  341.                      else if(*cp=='g') cmdp->flags.global =1;
  342.              else if(*cp=='p') cmdp->flags.print = 1;
  343.              else if(*cp== 'P') cmdp->flags.print = 2;
  344.                      else break;
  345.                      cp++;}
  346.        case 'l':            /* list pattern space */
  347.         if (*cp == 'w') cp++;/* and execute a w command! */
  348.         else    break;        /* s or l is done */
  349.       case 'w':            /* write-pattern-space command */
  350.       case 'W':            /* write-first-line command */
  351.         if (nwfiles >= WFILES)    ABORT(TMWFI);
  352.         fp = gettext(fname[nwfiles] = fp,0);/*get filename */ /*hh 18*/
  353.                 if(!*fname[nwfiles]){cmdp->fout=stdout;return 0;}/*dft stdout*/
  354.         for (i = nwfiles - 1; i >= 0; i--)    /* match it in table */
  355.              if (strcmp(fname[nwfiles],fname[i])==0) {/*could opt fp*/
  356.                 cmdp->fout = fout[i]; return (1);}
  357.           /* if didn't find one, open new out file */
  358.         if((cmdp->fout=fopen(fname[nwfiles],"wt"))==NULL){ /*hh 15*/
  359.             fprintf(stderr, CCOFI, fname[nwfiles]);    exit(2);}
  360.         fout[nwfiles++] = cmdp->fout;
  361.         break;
  362.        case 'y':            /* transliterate text */
  363.                 cmdp->u.lhs=fp;
  364.                 if(!ycomp()) ABORT(CGMSG);
  365.         if (fp > poolend)ABORT(TMTXT);    /* fail on overflow */
  366.         break;}    /* switch */
  367.     return 1;}        /* succeeded in interpreting one command */
  368.  
  369. static char tox(c) char c;{ /*hh 4-8*/
  370.   if(isdigit(c))return c&017;
  371.   else return (c&07)+9;}
  372.  
  373. static char fixquote(p)
  374. char **p;
  375. /*function added by h**2*/
  376. {
  377.     char c = *(*p-1);
  378.     char    x1,x2;
  379.  
  380.   if(c=='a')c='\a';  /* for all quoted replacements*/
  381.      else if(c=='b')c='\b';
  382.      else if(c=='e')c= 27;
  383.      else if(c=='f')c='\f';
  384.      else if(c=='n')c='\n';
  385.      else if(c=='r')c='\r';
  386.      else if(c=='t')c='\t';
  387.      else if(c=='v')c='\v';
  388.      else if(c=='x'){
  389.            if(!(x1= *(*p))||!(x2=*(*p+1))
  390.               ||!isxdigit(x1)||!isxdigit(x2))
  391.                           {fprintf(stderr,ILFQT,(*p)-2);exit(2);}
  392.                    c=(tox(x1)<<4)|tox(x2);(*p)+=2;}
  393.   return c;}  /*hh 4-8*/
  394.  
  395. static int     rhscomp(rhsp, delim)    /* uses bcount *//*hh 5*/
  396. /* generate replacement string for substitute command right hand side */
  397.     char  **rhsp;    /* place to compile expression to */
  398.     char   delim;    /* regular-expression end-mark to look for */
  399.      { if(lastrhs&&*cp=='%'&&cp[1]==delim){
  400.           *rhsp=lastrhs;cp+=2;return 1;}/* repeat last substitution*/ 
  401.        lastrhs=*rhsp=fp;
  402.        while((*fp=*cp++)!=delim)
  403.     if (*fp  == '\\') {    /* copy; if it's a \, */
  404.         *fp = *cp++;  /* copy escaped char */
  405.         /* check validity of pattern tag */
  406.         if(*fp>'0'&&*fp<='9'){/*hh 5*/
  407.                if(*fp>bcount+'0')return 0;
  408.                else *fp++=ARGMARK,*fp++=cp[-1];}
  409.         else if(!*fp)    return 0;
  410.         else *fp++=fixquote(&cp);
  411.         continue;}
  412.     else if(*fp =='&') *fp++=ARGMARK,*fp++='0'; /*note replacement*/
  413.     else if (*fp++ == '\0')    /* last ch not RE end, help! */
  414.         return 0;
  415.      *fp++ = '\0';            /* cap the expression string */
  416.      return 1;}
  417.  
  418. static int     recomp(expbuf, redelim)        /* uses cp, bcount */
  419. /* compile a regular expression to internal form */
  420.     char    **expbuf;        /* place to compile it to */
  421.     char    redelim;        /* RE end-marker to look for */
  422. {
  423.     char  *lastep= 0; /* for repeat handling */ /*hh 4*/
  424.     register int    c;    /* current-character pointer */
  425.     char    negclass;    /* all-but flag */
  426.     char    brnest[MAXTAGS];    /* bracket-nesting array */
  427.     char   *brnestp;    /* ptr to current bracket-nest */
  428.     int    classct;    /* class element count */
  429.     int    tags;        /* # of closed tags */
  430.     int lastc,nextc;        /*temps for ranges*/
  431.     if (*cp == redelim){    /* if first char is RE endmarker */
  432.         cp++;if(!lastre) return 0;
  433.         *expbuf=lastre; return 1;}
  434.     *expbuf=lastre =fp; /*if this is good its the last one we found*/
  435.     brnestp = brnest;        /* initialize ptr to brnest array */
  436.     tags = bcount = 0;        /* initialize counters */
  437.     if ( (*fp++ = (*cp == '^')) != 0)cp++; /* check for start-of-line  */
  438.     while(fp<poolend&&(c=*cp++)!=redelim  ){
  439.       switch (c) {
  440.            case '\\':
  441.         if ((c = *cp++) == '(') {    /* start tagged section */
  442.           if (bcount >= MAXTAGS) return 0;
  443.                   lastep=0;
  444.           *brnestp++ = bcount;        /* update tag stack */
  445.           *fp++ = CBRA;            /* enter tag-start */
  446.           *fp++ = bcount++ + 1;        /* bump tag count */
  447.           break;}
  448.         else if (c == ')') {        /* end tagged section */
  449.           if (brnestp <= brnest) return 0;/* extra \) */
  450.                   lastep=0;
  451.           *fp++ = CKET;            /* enter end-of-tag */
  452.           *fp++ = *--brnestp + 1;    /* pop tag stack */
  453.           tags++;            /* count closed tags */
  454.           break;}
  455.             else if(c=='{'){if(!lastep) return 0; /* rep error*/
  456.           *lastep|=MTYPE; lastep=0;
  457.           if(!processm())return 0;
  458.           break;}
  459.         else if(c=='<'){   /*begining of word test*/
  460.           lastep=0; *fp++=CBOW;
  461.           break;}
  462.         else if(c=='>'){/*end of word test*/
  463.           lastep=0; *fp++=CEOW;
  464.           break;}
  465.         else if (c >= '1' && c <= '9') {    /* tag use */
  466.           if ((c -= '1') >= tags) return 0 ;    /* too few */
  467.           lastep=fp; *fp++ = CBACK;        /* enter tag mark */
  468.           *fp++ = c+1;         /* and the number */
  469.           break;}
  470.         else if (c == '\n')return 0;    /* escaped newline bad*/
  471.         else { c=fixquote(&cp);
  472.                goto defchar;} /*hh 4*/
  473.            case '\0':        /* do not allow */
  474.            case '\n': return 0;    /* no trailing pattern delimiter */
  475.            case '.':       /* match any char except newline */
  476.         lastep=fp; *fp++ = CDOT;
  477.         break;
  478.            case '+':    /* 1 to n repeats of previous pattern */
  479.         if(!lastep)   goto defchar;
  480.         *lastep|=MTYPE; lastep=0; *fp++=1;*fp++=0xFF;
  481.         break;
  482.            case '*':    /* 0..n repeats of previous pattern */
  483.         if(!lastep) goto defchar;
  484.         *lastep|=STAR; lastep=0;
  485.         break;
  486.            case '$':    /* match only end-of-line */
  487.         if (*cp != redelim)        /* if we're not at end of RE */
  488.             goto defchar;        /* match a literal $ */
  489.         *fp++ = CDOL;            /* insert end-symbol mark */
  490.         break;
  491.            case '[':    /* begin character set pattern */
  492.         lastep=fp;
  493.         if (fp + 33 >= poolend) ABORT(REITL);
  494.         *fp++ = CCL;     /* insert class mark */
  495.         if ( (negclass = ((c = *cp++) == '^')) != 0)     /* v1.1 */
  496.             c = *cp++;
  497.         lastc=0;
  498.         do {if (c == '\0') ABORT(CGMSG);
  499.            /* handle character ranges */
  500.             if (c == '-' && lastc && *cp != ']'){
  501.               nextc=*cp++&0xff;
  502.               if(nextc=='\\')cp++,nextc=fixquote(&cp)&0xff;
  503.               if(lastc>nextc)ABORT(BADRANGE);
  504.               for(;lastc<=nextc;lastc++)fp[lastc>>3]|=bits[lastc&7];
  505.               lastc=0;continue;}
  506.             if (c == '\\')cp++,c=fixquote(&cp); /*hh 8*/
  507.             fp[(c>>3)&0x1F] |= bits[c & 7];lastc=c&0xff;
  508.             } while((c = *cp++) != ']');
  509.         /* invert the bitmask if all-but was specified */
  510.         if (negclass)
  511.            for (classct = 0; classct<32;classct++)fp[classct] ^= 0xFF;
  512.         fp[0] &= 0xFE;        /* never match ASCII 0 */
  513.         fp += 32;        /* advance ep past set mask */
  514.         break;
  515.          defchar:            /* match literal character */
  516.            default:            /* which is what we'd do by default */
  517.         lastep=fp;
  518.         *fp++ = CCHR;        /* insert character mark */
  519.         *fp++ = c;
  520.         break;}}
  521.     *fp++=CEOF;return fp<poolend&& brnestp==brnest;}
  522.  
  523. static int processm(Void) {int i1=0,i2=0;
  524.   while(isdigit(*cp))i1=i1*10+*cp++-'0';
  525.   if(!i1||i1>255)return 0;
  526.   *fp++ = (char)i1;
  527.   if(*cp=='\\'&&cp[1]=='}')cp+=2,*fp++=0;
  528.   else if(*cp==','&&cp[1]=='\\'&&cp[2]=='}')cp+=3,*fp++ = 0xFF;
  529.   else if(*cp++==','){
  530.     while (isdigit(*cp))i2=i2*10+*cp++-'0';
  531.     if(*cp!='\\'||cp[1]!='}'||i2<i1||i2-i1>254)return 0;
  532.     cp+=2;*fp++=(char)(i2-i1);}
  533.   else return 0;
  534.   return 1;}
  535.  
  536. static char *p=NULL;
  537.  
  538. static void einit(Void){
  539.     if (eargc-- <= 0){fprintf(stderr,NOARG);exit(2);} /*hh 12*/
  540.     p = *++eargv;}
  541.  
  542. static int ecmdline(Void)
  543. {
  544.     char *cbuf=linebuf-1;
  545.  
  546.     cp=linebuf;
  547.    if(p==NULL)return 0;
  548.    while ( (*++cbuf = *p++) != 0)    /* v1.4 */
  549.     if (*cbuf == '\\') {
  550.         if ((*++cbuf=*p++) == '\0'){*++cbuf=0;p=NULL;return 1;}
  551.         else  continue;}
  552.     else if (*cbuf == '\n') {/*end of cmd line*/
  553.         *cbuf = '\0';
  554.         return ( 1);}
  555.    p=NULL;
  556.    return 1;}
  557.  
  558. static int fcmdline(Void){ /*uses cmdf; read next command from file */
  559.     int inc, any=0; /*hh 19*/
  560.     char *cbuf=linebuf-1;        /* so pre-increment points us at cbuf */
  561.     cp=linebuf;
  562.     while ((inc = getc(cmdf)) != EOF)    /* get next char */
  563.       if (++any&&((*++cbuf = inc) == '\\'))    /* if it's escape *//*hh 19*/
  564.             *++cbuf = inc = getc(cmdf);    /* get next char */
  565.         else if (*cbuf == '\n')        /* end on newline */
  566.             return (*cbuf = '\0', 1);    /* cap the string */
  567.       return (*++cbuf = '\0', any); /*real end-of-file?*//*hh 19*/}
  568.  
  569. static int address(expbuf,pass)        /* uses cp, linenum */
  570. /* expand an address at *cp... into expbuf, return address ok */
  571.     char **expbuf;int pass;
  572. { static int numl = 0,numpl=0;/* current inds in addr-number tables */
  573.     int code;
  574.     char  *rcp;        /* temp compile ptr for forwd look */
  575.     long   lno;        /* computed value of numeric address */
  576.     *expbuf=fp;      /* nominally this is the address start*/
  577.     if (*cp == '$') {        /* end-of-source address */
  578.         *fp++ = CEND;    /* write symbolic end address */
  579.         *fp++ = CEOF;    /* and the end-of-address mark (!) */
  580.         cp++;            /* go to next source character */
  581.         return 1;}        /* we're done */
  582.     if (*cp == '/'||*cp=='\\'){    /* start of regular-expression match */
  583.         if(*cp=='\\')cp++;
  584.         if(!recomp(expbuf, *cp++)) ABORT(AGMSG);/* compile the RE */
  585.         else return 1;}
  586.     code=CLNUM;
  587.     if(pass==2&&*cp=='+'){cp++,code=CPLUS;} /*compile + in 2nd address*/
  588.     rcp = cp;
  589.     lno = 0;            /* now handle a numeric address */
  590.     while (*rcp >= '0' && *rcp <= '9')    /* collect digits */
  591.         lno = lno * 10 + *rcp++ - '0';    /* compute their value */
  592.     if (lno) {                /* if we caught a number... */
  593.         *fp++ = code;         /* put a numeric-address marker */
  594.         *fp++ = numl;          /* and the address table index */
  595.         linenum[numl++] = lno;        /* and set the table entry */
  596.         if (numl >= MAXLINES)    /* oh-oh, address table overflow */
  597.             ABORT(TMLNR);        /* abort with error message */
  598.         if(code==CPLUS){
  599.             *fp++=numpl++;
  600.             if(numpl>=MAXPLUS)ABORT(TMLNR);}
  601.         *fp++ = CEOF;    /* write the end-of-address marker */
  602.         cp = rcp;        /* point compile past the address */
  603.         return 1;        /* we're done */}
  604.     *expbuf=NULL;
  605.     return 0;}            /* no legal address was found */
  606.  
  607. static char    *gettext(txp,quoting)    /* uses global cp *//*hh 4-5,12,15*/
  608. /* accept multiline input from *cp..., discarding leading whitespace */
  609.     register char  *txp; int quoting; /* where to put the text */
  610. {       SKIPWS(cp);txp--;
  611.       while(*++txp=*cp++){
  612.           if(!quoting&&(*txp=='#'||*txp==';')){*txp=0;break;}
  613.           if(*txp=='\\'&"ing)cp++,*txp=fixquote(&cp);
  614.           if(*txp=='\n')SKIPWS(cp);}
  615.          return (cp--,++txp);}
  616.  
  617. static label   *search(Void){label *l; char *lname;    /* uses global curlab */
  618.     SKIPWS(cp);
  619.     fp=gettext(lname=curlab->name=fp,0);
  620.         if(!*lname) return labels;
  621.         for(l=labels+1;strcmp(l->name,lname);l++) ;
  622.     if(l==curlab){if(++curlab>=labels+MAXLABS)ABORT(TMLAB);}
  623.         else fp=lname;
  624.         return l;}
  625.  
  626. static void resolve(Void)
  627. /*hh 14,15*/
  628. {
  629.     label *l=labels;
  630.     sedcmd *f,*t;
  631.  
  632.     l->address=cmdp;
  633.     while(l<curlab){
  634.         if(!l->address){fprintf(stderr,ULABL,l->name);exit(2);}
  635.         if(!(f=l->list))
  636.         {
  637.             if(l!=labels)
  638.                 fprintf(stderr,"sed: Label not used %s\n",l->name);
  639.         }
  640.         else  do{
  641.             t=f->u.link;f->u.link=l->address;
  642.         }
  643.         while( (f=t) != NULL);    /* v1.4 */
  644.         l++;
  645.     }
  646. }
  647.  
  648. static int    ycomp(Void)
  649. /* compile a y (transliterate) command */
  650. {    char   *tp, *sp,delim=*cp++; int c; /*hh 6*/
  651.     /* scan the 'from' section for invalid chars */
  652.     for (sp = tp = cp; *tp != delim; tp++) {
  653.         if (*tp == '\\')  tp++;
  654.         if ((*tp == '\n') || (*tp == '\0'))return 0;}
  655.     tp++;    /* tp now points at first char of 'to' section */
  656.     /* now rescan the 'from' section */
  657.     while ((c = *sp++&0xff) != delim) {
  658.       if (c == '\\') sp++,c=fixquote(&sp)&0xff;
  659.       if ((fp[c]=*tp++)=='\\')tp++,fp[c]=fixquote(&tp); /*hh 6*/
  660.       else if ((fp[c] == delim) || (fp[c] == '\0'))return 0;    }
  661.     if (*tp != delim)    /* 'to', 'from' parts have unequal lengths */
  662.         return 0;
  663.     cp = ++tp;            /* point compile ptr past translit */
  664.     for (c = 0; c < 256; c++)  /* fill in self-map entries in table */
  665.         if (fp[c] == 0) fp[c] = c;
  666.         fp+=0x100;
  667.     return 1;}  
  668.  
  669. /* sedcomp.c ends here */
  670.